package util;


import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;

public abstract class JasonUtil {
    
    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * Convert an object to joson format text.
     * 
     * @param obj The object to be converted to jason
     * @return Joson format text
     * @throws IOException
     */
    public static String toJson(Object obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonGenerator jsonGenerator = MAPPER.getFactory().createGenerator(baos, JsonEncoding.UTF8);
        MAPPER.setSerializationInclusion(NON_EMPTY);
        MAPPER.setSerializationInclusion(NON_NULL);
        jsonGenerator.writeObject(obj);
        String json = baos.toString();
        baos.close();
        return json;
    }

    public static String mapToJson(Map<String, ?> map) throws IOException {
        String json = MAPPER.writeValueAsString(map);
        return json;
    }

    public static Map<String, Object> jsonToMap(String json)
            throws JsonParseException, JsonMappingException, IOException {
        Map<String, Object> map = MAPPER.readValue(json, new TypeReference<LinkedHashMap<String, Object>>() {});
        return map;
    }

    /**
     * Convert jason format text to object.
     * 
     * @param jason Jason format text
     * @param clazz Class of the target object.
     * @return The object what convert from jason string
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws IOException
     */
    public static <T extends Object> T fromJson(String jason, Class<T> clazz)
            throws JsonParseException, JsonMappingException, IOException {
        if (StringUtils.isEmpty(jason) || clazz == null) {
            return null;
        } else {
            T obj = MAPPER.readValue(jason, clazz);
            return obj;
        }

    }

    @SuppressWarnings("unchecked")
    public static <T extends Collection<?>> T jasonToCollection(String src, Class<T> collectionClass,
            Class<?>... valueType) throws Exception {
        JavaType javaType = MAPPER.getTypeFactory().constructParametricType(collectionClass, valueType);
        return (T) MAPPER.readValue(src, javaType);
    }

    public static String toJson(String obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonGenerator jsonGenerator = MAPPER.getJsonFactory().createJsonGenerator(baos, JsonEncoding.UTF8);
        jsonGenerator.writeObject(obj);
        String json = baos.toString();
        baos.close();
        return json;
    }


    /**
     * 单位缩进字符串。
     */
    private static String SPACE = "&nbsp;";

    /**
     * 返回格式化JSON字符串。
     * 
     * @param json 未格式化的JSON字符串。
     * @return 格式化的JSON字符串。
     */
    public static String formatJson(String json) {
        if (json == null) {
            return "json不能为空";
        }
        StringBuffer result = new StringBuffer();

        int length = json.length();
        int number = 0;
        char key = 0;

        // 遍历输入字符串。
        for (int i = 0; i < length; i++) {
            // 1、获取当前字符。
            key = json.charAt(i);

            // 2、如果当前字符是前方括号、前花括号做如下处理：
            if ((key == '[') || (key == '{')) {
                // （1）如果前面还有字符，并且字符为“：”，打印：换行和缩进字符字符串。
                if ((i - 1 > 0) && (json.charAt(i - 1) == ':')) {
                    result.append("<br/>");
                    result.append(indent(number));
                }

                // （2）打印：当前字符。
                result.append(key);

                // （3）前方括号、前花括号，的后面必须换行。打印：换行。
                result.append("<br/>");

                // （4）每出现一次前方括号、前花括号；缩进次数增加一次。打印：新行缩进。
                number++;
                result.append(indent(number));

                // （5）进行下一次循环。
                continue;
            }

            // 3、如果当前字符是后方括号、后花括号做如下处理：
            if ((key == ']') || (key == '}')) {
                // （1）后方括号、后花括号，的前面必须换行。打印：换行。
                result.append("<br/>");

                // （2）每出现一次后方括号、后花括号；缩进次数减少一次。打印：缩进。
                number--;
                result.append(indent(number));

                // （3）打印：当前字符。
                result.append(key);

                // （4）如果当前字符后面还有字符，并且字符不为“，”，打印：换行。
                if (((i + 1) < length) && (json.charAt(i + 1) != ',')) {
                    result.append("<br/>");
                }

                // （5）继续下一次循环。
                continue;
            }

            // 4、如果当前字符是逗号。逗号后面换行，并缩进，不改变缩进次数。
            if ((key == ',')) {
                result.append(key);
                result.append("<br/>");
                result.append(indent(number));
                continue;
            }

            // 5、打印：当前字符。
            result.append(key);
        }

        return result.toString();
    }

    /**
     * 返回指定次数的缩进字符串。每一次缩进三个空格，即SPACE。
     * 
     * @param number 缩进次数。
     * @return 指定缩进次数的字符串。
     */
    private static String indent(int number) {
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < number * 5 + 2; i++) {
            result.append(SPACE);
        }
        return result.toString();
    }
}
